// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/app_list/pagination_controller.h"

#include "ui/app_list/pagination_model.h"
#include "ui/events/event.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/vector2d.h"

namespace app_list {

namespace {

    // Constants for dealing with scroll events.
    const int kMinScrollToSwitchPage = 20;
    const int kMinHorizVelocityToSwitchPage = 800;

    const double kFinishTransitionThreshold = 0.33;

} // namespace

PaginationController::PaginationController(PaginationModel* model,
    ScrollAxis scroll_axis)
    : pagination_model_(model)
    , scroll_axis_(scroll_axis)
{
}

bool PaginationController::OnScroll(const gfx::Vector2d& offset,
    ScrollEventType type)
{
    int offset_magnitude;
    if (scroll_axis_ == SCROLL_AXIS_HORIZONTAL) {
        // If the view scrolls horizontally, both horizontal and vertical scroll
        // events are valid (since most mouse wheels only have vertical scrolling).
        offset_magnitude = abs(offset.x()) > abs(offset.y()) ? offset.x() : offset.y();
    } else {
        // If the view scrolls vertically, only vertical scroll events are valid.
        offset_magnitude = offset.y();
    }

    // Do not scroll on very small touchpad events. Mouse wheel events should
    // scroll, no matter how small the value change.
    if (type == SCROLL_MOUSE_WHEEL || abs(offset_magnitude) > kMinScrollToSwitchPage) {
        if (!pagination_model_->has_transition()) {
            pagination_model_->SelectPageRelative(offset_magnitude > 0 ? -1 : 1,
                true);
        }
        return true;
    }

    return false;
}

bool PaginationController::OnGestureEvent(const ui::GestureEvent& event,
    const gfx::Rect& bounds)
{
    const ui::GestureEventDetails& details = event.details();
    switch (event.type()) {
    case ui::ET_GESTURE_SCROLL_BEGIN:
        pagination_model_->StartScroll();
        return true;
    case ui::ET_GESTURE_SCROLL_UPDATE: {
        float scroll = scroll_axis_ == SCROLL_AXIS_HORIZONTAL
            ? details.scroll_x()
            : details.scroll_y();
        int width = scroll_axis_ == SCROLL_AXIS_HORIZONTAL ? bounds.width()
                                                           : bounds.height();
        // scroll > 0 means moving contents right or down. That is, transitioning
        // to the previous page.
        pagination_model_->UpdateScroll(scroll / width);
        return true;
    }
    case ui::ET_GESTURE_SCROLL_END:
        pagination_model_->EndScroll(pagination_model_->transition().progress < kFinishTransitionThreshold);
        return true;
    case ui::ET_SCROLL_FLING_START: {
        float velocity = scroll_axis_ == SCROLL_AXIS_HORIZONTAL
            ? details.velocity_x()
            : details.velocity_y();
        pagination_model_->EndScroll(true);
        if (fabs(velocity) > kMinHorizVelocityToSwitchPage)
            pagination_model_->SelectPageRelative(velocity < 0 ? 1 : -1, true);
        return true;
    }
    default:
        return false;
    }
}

} // namespace app_list
